package bexpred;
import java.util.*;

/**
 * Class for conversion boolean expression into R-M form
 * @author Tomasz Werszko
 *
 */
public class RM {
	
	protected Boolean[][] truthTable = null;
	protected int rows = 0;
	protected ArrayList<String> varInputs = new ArrayList<String>();
	protected Boolean[] RMFactors;
	
	/**
	 * 
	 * @return number of variables in expression
	 */
	public int getNumberOfVariables(){
		return varInputs.size();
	}
	
	/**
	 * Default constructor
	 */
	public RM(){
		
	}
	/**
	 * Constructor
	 * @param truthTable - truth table
	 * @param varInputs - list with input labels
	 */
	public RM(Boolean truthTable[][], List<String> varInputs) throws BExprException{
		//copying truth table and input variables
		this.copyData(truthTable, varInputs);
	    
	    //calculating factors
	    calculateRMFactors();
	}
	
	public RM(String expression) throws BExprException{
		// Create the expression tree from the expression.
		BExprTree bexprtree;
	    try {
	    	bexprtree = new BExprTree(expression);
	    }catch (BExprPreParseException ex) {
	    	throw new BExprException(ex.getMessage());
	    }
	    
	    ArrayList<String> varInputs = bexprtree.getVars();
		TruthTable TT = new TruthTable(bexprtree);
		Boolean[][] truthTable = TT.getWrappedTable();
		
		//copying truth table and input variables
		this.copyData(truthTable, varInputs);
	    
	    //calculating factors
	    calculateRMFactors();
		
	}
	
	/**
	 * Constructor
	 * @param bexprtree - expression Tree
	 * @throws Exception
	 */
	public RM(BExprTree bexprtree) throws BExprException{
		ArrayList<String> varInputs = bexprtree.getVars();
		TruthTable TT = new TruthTable(bexprtree);
		Boolean[][] truthTable = TT.getWrappedTable();
		
		//copying truth table and input variables
		this.copyData(truthTable, varInputs);
	    
	    //calculating factors
	    calculateRMFactors();
		
	}
	
	protected void copyData(Boolean truthTable[][], List<String> varInputs) throws BExprException{
		this.rows = truthTable.length;
	    
	    if(truthTable.length == 0){
	    	throw new BExprException("Incorrect size of the truth table.");
	    }
	    
	    if(varInputs.size() < truthTable[0].length - 1){
	    	throw new BExprException("Incorrect size of the truth table.");
	    }
	    
	    //copying truth table
	    this.truthTable = new Boolean[this.rows][truthTable[0].length];
	    for(int i = 0; i < this.rows; i++){
	    	for(int j = 0; j < truthTable[i].length; j++){
	    		this.truthTable[i][j] = truthTable[i][j];
	    	}
	    }
	    
	    //copying input labels
	    Iterator<String> iterator = varInputs.iterator();
	    while(iterator.hasNext()){
	    	String element = iterator.next();
	    	this.varInputs.add(new String(element));
	    }
	}
	
	/**
	 * Returns list with R-M factors
	 * @return list with R-M Factors
	 */
	public Boolean[] getRMFactors(){
		Boolean[] result = new Boolean[this.rows];
		
		for(int i = 0; i < this.RMFactors.length; i++){
			result[i] = new Boolean(this.RMFactors[i]);
		}
		
		return result;
	}
	
	/**
	 * Calculates R-M factors
	 * @throws Exception
	 */
	public void calculateRMFactors() throws BExprException{
		if(this.truthTable.length == 0){
			throw new BExprException("Incorrect size of the truth table.");
	    }
	    
	    if(varInputs.size() < truthTable[0].length - 1){
	    	throw new BExprException("Incorrect size of the truth table.");
	    }
	    
	    Boolean[] result = new Boolean[this.rows];
	    //temporary arrays
	    Boolean[] temp1 = new Boolean[this.rows];
	    Boolean[] temp2;
	    
	    //preparing truth vector
	    for(int i = 0; i < this.rows; i++){
	    	temp1[i] = this.truthTable[i][this.truthTable[i].length - 1];
	    }
	    
	    //calculating vectors
	    result[0] = temp1[0];
	    for(int i = 0; i < rows-1; i++){
	    	temp2 = new Boolean[rows-i-1];
	    	for(int j = 0; j < (rows-1-i); j++){
	    		temp2[j] = temp1[j].booleanValue() ^ temp1[j+1].booleanValue();
	    	}
	    	temp1 = temp2;
	    	result[i+1] = temp1[0];
	    }
	    
	    //storing the result in RMFactors
	    this.RMFactors = result;
	}
	
	/**
	 * Returns readable version of R-M expression
	 * @return readable form of boolean expression
	 */
	public String toString(){
		String result = new String();
		ArrayList<String> list = new ArrayList<String>();
		//saves information about number of variables in every part of expression
		ArrayList<Integer> elementCounter = new ArrayList<Integer>();
		
		//checking all rows of the truth table
		for(int i = 0; i < this.rows; i++){
			Boolean[] vector = this.truthTable[i];
			elementCounter.add(new Integer(0));
			String string = "";
			//if all inputs are false
			if(i == 0 && this.RMFactors[i] == true){
				string += "'1'";
				elementCounter.set(i, new Integer(elementCounter.get(i).intValue() + 1));
			}
			//if not all inputs are false
			else if(this.RMFactors[i] == true){
				for(int j = 0; j < (vector.length - 1); j++){
					if(vector[j] == true){
						if(string.length() > 0){
							string += "*";
						}
						string += this.varInputs.get(j);
						elementCounter.set(i, new Integer(elementCounter.get(i).intValue() + 1));
					}
				}
			}
			list.add(new String(string));
		}
		
		//assembling final string
		Iterator<String> iterator = list.iterator();
		int i = 0;
		while(iterator.hasNext()){
			String element = iterator.next();
			
			if(result.length() > 0 && element.length() > 0){
				result += " ^ ";
			}
			if(element.length() > 0 && elementCounter.get(i) > 1){
				result += "(";
			}
			result += element;
			if(element.length() > 0 && elementCounter.get(i) > 1){
				result += ")";
			}
			i++;
		}
		return result;
	}
	
	/**
	 * Returns readable version of R-M expression without logic 1
	 * @return readable form of boolean expression
	 */
	public String toStringW1(){
		String result = new String();
		ArrayList<String> list = new ArrayList<String>();
		//saves information about number of variables in every part of expression
		ArrayList<Integer> elementCounter = new ArrayList<Integer>();
		
		//checking all rows of the truth table
		for(int i = 0; i < this.rows; i++){
			Boolean[] vector = this.truthTable[i];
			elementCounter.add(new Integer(0));
			String string = "";
			//if all inputs are false
			if(i == 0 && this.RMFactors[i] == true){
			//	string += "'1'";
			//	elementCounter.set(i, new Integer(elementCounter.get(i).intValue() + 1));
			}
			//if not all inputs are false
			else if(this.RMFactors[i] == true){
				for(int j = 0; j < (vector.length - 1); j++){
					if(vector[j] == true){
						if(string.length() > 0){
							string += "*";
						}
						string += this.varInputs.get(j);
						elementCounter.set(i, new Integer(elementCounter.get(i).intValue() + 1));
					}
				}
			}
			list.add(new String(string));
		}
		
		//assembling final string
		Iterator<String> iterator = list.iterator();
		int i = 0;
		while(iterator.hasNext()){
			String element = iterator.next();
			
			if(result.length() > 0 && element.length() > 0){
				result += " ^ ";
			}
			if(element.length() > 0 && elementCounter.get(i) > 1){
				result += "(";
			}
			result += element;
			if(element.length() > 0 && elementCounter.get(i) > 1){
				result += ")";
			}
			i++;
		}
		return result;
	}
}